package kr.ac.konkuk.dblab.point.utils;

import java.awt.geom.Point2D;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import kr.ac.konkuk.dblab.point.metadata.Quad;
import kr.ac.konkuk.dblab.point.metadata.Rect;
import kr.ac.konkuk.dblab.point.quadtree.NodeType;

public class PointUtils {
	public static Rect makeRectFromPoints(Collection<? extends Point2D> points,
			Point2D _lt, Point2D _rb, Point2D _center) {
		Point2D lt, rb;
		if (_lt != null) {
			lt = _lt;
		} else {
			lt = new Point2D.Double();
		}

		if (_rb != null) {
			rb = _rb;
		} else {
			rb = new Point2D.Double();
		}

		lt.setLocation(Double.MAX_VALUE, Double.MAX_VALUE);
		rb.setLocation(Double.MIN_VALUE, Double.MIN_VALUE);

		for (Point2D p : points) {
			if (lt.getX() > p.getX()) {
				lt.setLocation(p.getX(), lt.getY());
			}

			if (lt.getY() > p.getY()) {
				lt.setLocation(lt.getX(), p.getY());
			}

			if (rb.getX() < p.getX()) {
				rb.setLocation(p.getX(), rb.getY());
			}

			if (rb.getY() < p.getY()) {
				rb.setLocation(rb.getX(), p.getY());
			}
		}

		if (_center != null) {
			Point2D center = makeCenter(lt, rb);
			_center.setLocation(center);
		}

		return new Rect(lt, rb);
	}

	public static Rect makeRectFromPoints(Collection<? extends Point2D> points,
			Point2D center) {
		return makeRectFromPoints(points, null, null, center);
	}

	public static Rect makeRectFromPoints(Collection<? extends Point2D> points) {
		return makeRectFromPoints(points, null, null, null);
	}

	public static Point2D makeCenter(Point2D lb, Point2D rt) {
		return new Point2D.Double((lb.getX() + rt.getX()) / 2,
				(lb.getY() + rt.getY()) / 2);
	}

	public static Map<NodeType, Quad<Point2D>> makeQuad(
			Collection<? extends Point2D> points, Point2D center, Rect rect) {
		Set<Point2D> addedPoints = new HashSet<Point2D>();
		Map<NodeType, Quad<Point2D>> quads = new EnumMap<NodeType, Quad<Point2D>>(
				NodeType.class);
		quads.put(NodeType.LEFT_TOP,
				new Quad<Point2D>(new Rect(rect.left, rect.top, center.getX(),
						center.getY()), new TreeSet<Point2D>()));
		quads.put(NodeType.LEFT_BOTTOM, new Quad<Point2D>(new Rect(rect.left,
				center.getY(), center.getX(), rect.bottom),
				new TreeSet<Point2D>()));
		quads.put(NodeType.RIGHT_TOP, new Quad<Point2D>(new Rect(center.getX(),
				rect.top, rect.right, center.getY()),
				new TreeSet<Point2D>()));
		quads.put(NodeType.RIGHT_BOTTOM, new Quad<Point2D>(new Rect(center.getX(),
				center.getY(), rect.right, rect.bottom),
				new TreeSet<Point2D>()));

		for (Point2D p : points) {
			// left quads
			if (p.getX() >= rect.left && p.getX() <= center.getX()) {
				// left top quad
				if (p.getY() <= rect.top && p.getY() > center.getY()) {
					add(p, quads.get(NodeType.LEFT_TOP).getPoints(), addedPoints);
				}

				// left bottom quad
				if (p.getY() >= rect.bottom && p.getY() <= center.getY()) {
					add(p, quads.get(NodeType.LEFT_BOTTOM).getPoints(), addedPoints);
				}
			}

			// right quads
			if (p.getX() <= rect.right && p.getX() > center.getX()) {
				// right top quad
				if (p.getY() <= rect.top && p.getY() > center.getY()) {
					add(p, quads.get(NodeType.RIGHT_TOP).getPoints(), addedPoints);
				}

				// right bottom quad
				if (p.getY() >= rect.bottom && p.getY() <= center.getY()) {
					add(p, quads.get(NodeType.RIGHT_BOTTOM).getPoints(), addedPoints);
				}
			}
		}

		return quads;
	}

	private static void add(Point2D p, Set<Point2D> points,
			Set<Point2D> addedPoints) {
		if (addedPoints.contains(p)) {
			points.add(p);
			addedPoints.add(p);
		}
	}
}
